package com.macro.mall.biz;

import com.macro.mall.constant.CommonUtil;
import com.macro.mall.exception.BusinessException;
import com.macro.mall.service.IdGeneratorService;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

@Component
public class IdGeneratorBiz {

    private static final Logger logger = LoggerFactory.getLogger(IdGeneratorBiz.class);

    private String serverSeq;

    private Long baseFlowSeq;

    private String serverSeqForOrder;

    private Long baseFlowSeqForOrder;

    @Autowired
    private IdGeneratorService idGeneratorService;

    @Value("${zookeeper.nodename}")
    private String nodename;

    @Value("${zookeeper.nodename-order}")
    private String nodenameForOrder;

    @Value("${floorMod-divisor}")
    private int floorModDivisor;

    @Autowired
    private ZooKeeper zkClient;

    /**
     * Autowired 加载完以后执行
     */
    @PostConstruct
    public void init() {
        serverSeq = getServerSeq();
        baseFlowSeq = getRandomNumber(8);

        serverSeqForOrder = getServerSeqForOrder();
        baseFlowSeqForOrder = getRandomNumber(4);
    }

    /**
     *  ID生成规则: nowDate(14)+dataFlag(2)+busiCode(4)+serverSeq(2)+flowSeq(10)
     *     nowDate   yyyyMMddHHmmss
     *     dataFlag  通过userId计算floorMod获得
     *     busiCode  业务编码，用于区分流水的用途。比如订单流水1000， boss网关日志流水2000
     *          请求传递，如果长度超过4位 截取末4位 不足4位前面补零凑够4位
     *     serverSeq  服务启动 从数据库表t_server_seq中获取自增主键序列保留后2位
     *          需要关联zookeeper集群去判断IdGeneratorService服务集群之间serverSeq不重复
     *     flowSeq  服务维护自增(内存),初始化random(8位随机数) 基于这个随机数每次请求+1 前面补全两个0
     */
     public String getIdGenerator(Long userId,String busCode){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(getNowDate());
        stringBuilder.append(getDataFlag(userId));
        stringBuilder.append(getBuisCode(busCode));
        stringBuilder.append(serverSeq);
        stringBuilder.append(getFlowSeq());
        return stringBuilder.toString();
    }



    /**
     * 获取当前日期
     */
    private String getNowDate(){
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        String nowDate = dtf.format(LocalDateTime.now());
        return nowDate;
    }

    /**
     *  获取业务编码
     */
    private String getBuisCode(String busiCode){
        if(busiCode.length()>=4){
            return busiCode.substring(busiCode.length()-4);//长度超过4位 截取末4位
        }else{
            return String.format("%04d", Integer.valueOf(busiCode));//不足4位前面补零凑够4位
        }
    }

    /**
     *  根据userId获取分库标识
     */
    private String getDataFlag(Long userId){
        try {
            return String.format("%02d", Math.floorMod(userId, floorModDivisor));
        }catch (BusinessException e){
            e.printStackTrace();
            return "01";
        }
    }

    /**
     * 获取serverSeq
     */
    private String getServerSeq(){
        String seqId = idGeneratorService.getTbServerSeqId().toString();
        String tmpServerSeq = "";
        if(seqId.length()>=2){
            tmpServerSeq = seqId.substring(seqId.length()-2);//长度超过2位 截取末2位
        }else{
            tmpServerSeq = String.format("%02d", Integer.valueOf(seqId));//不足2位前面补零凑够2位
        }
        //写入zookeeper
        if(createZookeeperNode("SS"+tmpServerSeq)){
            return tmpServerSeq;
        }else{
            return getServerSeq();
        }
    }

    //获取随机数
    private static Long getRandomNumber(int digit){
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < digit; i++) {
            if (i == 0 && digit > 1)
                str.append(new Random().nextInt(9) + 1);
            else
                str.append(new Random().nextInt(10));
        }
        return Long.valueOf(str.toString());
    }
    /**
     *  获取flowSeq
     */
    private synchronized String getFlowSeq(){
        baseFlowSeq = baseFlowSeq + 1;
        if(baseFlowSeq > 9999999999l){
            baseFlowSeq = 1l;
        }
        return String.format("%010d", baseFlowSeq);//不满10位前面补零
    }

    /**
     * 创建zookeeper的节点，保存两位ServerSeq到节点下面的子节点
     *      true  创建新节点
     *      false  没有创建新节点(异常，重复)
     */
    private boolean createZookeeperNode(String serverSeq){
        try {
            if(zkClient.exists(nodename,true) == null){
                //创建持久化节点
                String nodedata = nodename.replace("/","");
                zkClient.create(nodename,nodedata.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            List<String> list = zkClient.getChildren(nodename, true);
            List<String> filterList = list.stream().filter(t -> t.equals(serverSeq)).collect(Collectors.toList());
            if(filterList.isEmpty()){
                //创建临时节点  断开则删除
                zkClient.create(nodename+"/"+serverSeq,serverSeq.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);//临时节点
                //zkClient.create(nodename+"/"+serverSeq,serverSeq.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//永久节点
                return true;
            }else{
                return false;
            }
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            return false;
        }
    }

    /**
     *  ID生成规则: serverSeq(1)+dataFlag(1)+nowDate(12)+flowSeq(6)
     *     serverSeq  服务启动 从数据库表t_server_seq中获取自增主键序列保留后2位
     *          需要关联zookeeper集群去判断IdGeneratorService服务集群之间serverSeq不重复
     *     dataFlag  orderType=0 通过userId计算floorMod获得
     *               orderType=1, 5G订单- dataflag=6
     *               orderType=2, 权益订单(积分商城)-dataflag=7
     *               orderType=3, 权益订单(集运平台)-dataflag=8
     *     nowDate   yyMMddHHmmss
     *     flowSeq  服务维护自增(内存),初始化random(4位随机数) 基于这个随机数每次请求+1 前面补全两个0
     */
    public String getOrderIdGenerator(Long userId,String orderType){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(serverSeqForOrder);
        if(orderType.equals(CommonUtil.OrderType.COMMON)){
            stringBuilder.append(getDataFlagForOrder(userId));
        }else if(orderType.equals(CommonUtil.OrderType.GOLD)){
            stringBuilder.append("6");
        }else if(orderType.equals(CommonUtil.OrderType.BENEFITS_JF)){
            stringBuilder.append("7");
        }else if(orderType.equals(CommonUtil.OrderType.BENEFITS_JY)){
            stringBuilder.append("8");
        }
        stringBuilder.append(getNowDateForOrder());
        stringBuilder.append(getFlowSeqForOrder());
        return stringBuilder.toString();
    }

    /**
     * 获取serverSeq的id个位数
     */
    private String getServerSeqForOrder(){
        String seqId = idGeneratorService.getTbServerSeqId()%10+"";
        if(seqId.equals("0")){
            return getServerSeqForOrder();
        }
        //写入zookeeper
        if(createZookeeperNodeForOrder("SSForOrder"+seqId)){
            return seqId;
        }else{
            return getServerSeqForOrder();
        }
    }

    /**
     * 创建zookeeper的节点，保存两位ServerSeq到节点下面的子节点
     *      true  创建新节点
     *      false  没有创建新节点(异常，重复)
     */
    private boolean createZookeeperNodeForOrder(String serverSeq){
        try {
            if(zkClient.exists(nodenameForOrder,true) == null){
                //创建持久化节点
                String nodedata = nodenameForOrder.replace("/","");
                zkClient.create(nodenameForOrder,nodedata.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            List<String> list = zkClient.getChildren(nodenameForOrder, true);
            List<String> filterList = list.stream().filter(t -> t.equals(serverSeq)).collect(Collectors.toList());
            if(filterList.isEmpty()){
                //创建临时节点  断开则删除
                zkClient.create(nodenameForOrder+"/"+serverSeq,serverSeq.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);//临时节点
                //zkClient.create(nodename+"/"+serverSeq,serverSeq.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);//永久节点
                return true;
            }else{
                return false;
            }
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            return false;
        }
    }

    /**
     *  根据userId获取分库标识
     */
    private String getDataFlagForOrder(Long userId){
        try {
            return Math.floorMod(userId, floorModDivisor)+"";
        }catch (BusinessException e){
            e.printStackTrace();
            return "0";
        }
    }

    /**
     * 获取当前日期
     */
    private String getNowDateForOrder(){
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyMMddHHmmss");
        String nowDate = dtf.format(LocalDateTime.now());
        return nowDate;
    }

    /**
     *  获取flowSeq
     */
    private synchronized String getFlowSeqForOrder(){
        baseFlowSeqForOrder = baseFlowSeqForOrder + 1;
        if(baseFlowSeqForOrder>999999l){
            baseFlowSeqForOrder = 1l;
        }
        return String.format("%06d", baseFlowSeqForOrder);//不满6位前面补零
    }

    /**
     *  porderId生成规则: 实物类P开头， 虚拟类V开头，异常订单R开头，后面14位格式： yymmdd+8位数字(自增序列)
     *   porderType：实物类:P， 虚拟类:V  异常:R
     */
    @Transactional
    public synchronized String getPorderIdGenerator(String porderType){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(porderType);
        stringBuilder.append(getNowDateForPorder());
        long seqId = idGeneratorService.getTbPorderSeqId();
        String tmpSeq = seqId+"";
        if(seqId > 99999999l){
            tmpSeq = tmpSeq.substring(tmpSeq.length()-8,tmpSeq.length());//超过8位取前8位
        }else{
            tmpSeq = String.format("%08d", seqId);//不满8位前面补零

        }
        stringBuilder.append(tmpSeq);
        return stringBuilder.toString();
    }

    /**
     * 获取当前日期
     */
    private String getNowDateForPorder(){
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyMMdd");
        String nowDate = dtf.format(LocalDateTime.now());
        return nowDate;
    }

}
